﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace GpuProgramCompiler
{
    class GpuProgramCompiler
    {
		void OutputShader(CmdLineArgs args, CompiledShader shader)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(args["OUT"]));
            FileStream stream = new FileStream(args["OUT"], FileMode.Create);
            BinaryWriter writer = new BinaryWriter(stream);
            if (writer != null)
            {
                UInt32 shaderHeaderCrc = Crc.GetInstance().GenerateCrc("GPUP");
                writer.Write(shaderHeaderCrc);
                writer.Write((UInt32)shader.Programs.Count);
				writer.Write((UInt32)shader.ConstantTables.Count);

                foreach (CompiledShader.Program program in shader.Programs)
                {
                    writer.Write(Crc.GetInstance().GenerateCrc(program.Type));
                    writer.Write(program.Code.Length);
                    writer.Write(program.Code);
                }

				foreach (CompiledShader.ConstantTable constantTable in shader.ConstantTables)
				{
					if (constantTable.Constants.Count > 0)
					{
						writer.Write(Crc.GetInstance().GenerateCrc(constantTable.Name));
						writer.Write(constantTable.Constants.Count);

						foreach (CompiledShader.Constant constant in constantTable.Constants)
						{
							UInt32 nameCRC = Crc.GetInstance().GenerateCrc(constant.Name);
							UInt32 typeCRC = Crc.GetInstance().GenerateCrc(constant.Type);
							writer.Write(nameCRC);
							writer.Write(typeCRC);
							writer.Write(constant.Offset);
							writer.Write(constant.Size);
						}
					}
				}

                writer.Close();
            }
		}

        int Run(string[] args)
        {
			try
			{
				CmdLineArgs cmdLineArgs = new CmdLineArgs();

				cmdLineArgs.SupportedArgs.Add(new CmdLineArgs.SupportedArg("IN", "Input filename", CmdLineArgs.SupportedArg.RequirementType.Required));
				cmdLineArgs.SupportedArgs.Add(new CmdLineArgs.SupportedArg("OUT", "Output filename", CmdLineArgs.SupportedArg.RequirementType.Required));
				cmdLineArgs.SupportedArgs.Add(new CmdLineArgs.SupportedArg("PLATFORM", "Platform (DX9/DX10/DX11/OpenGL)", CmdLineArgs.SupportedArg.RequirementType.Required));
				cmdLineArgs.SupportedArgs.Add(new CmdLineArgs.SupportedArg("DEBUGINFO", "Enable debug info", CmdLineArgs.SupportedArg.RequirementType.Optional));

				if (cmdLineArgs.Initialise(args))
				{
					// Output shader
					CompiledShader outShader = new CompiledShader();

					// Quick check on files
					switch (cmdLineArgs["PLATFORM"])
					{
						case "DX9":
							{
								new Compilers.ShaderCompilerDX9(cmdLineArgs, ref outShader);
								break;
							}
						case "DX10":
							{
								new Compilers.ShaderCompilerDX10(cmdLineArgs, ref outShader);
								break;
							}
						case "DX11":
							{
								new Compilers.ShaderCompilerDX11(cmdLineArgs, ref outShader);
								break;
							}
						case "OpenGL":
							{
								new Compilers.ShaderCompilerOpenGL(cmdLineArgs, ref outShader);
								break;
							}
						default:
							{
								System.Console.WriteLine("Unknown platform '" + cmdLineArgs["PLATFORM"] + "'");
								break;
							}
					}

					OutputShader(cmdLineArgs, outShader);
				}
				else
				{
					System.Console.WriteLine("GpuProgramCompiler v0.1\n\nCompiles an FX file to a platform specific shader binary\n");
					cmdLineArgs.PrintUsage();
                    return 1;
				}
			}
			catch (Exception e)
			{
				System.Console.WriteLine("GpuProgramCompiler Error!\n\n" + e.Message);
				return 2;
			}

			return 0;
        }
	
		static int Main(string[] args)
		{
			GpuProgramCompiler compiler = new GpuProgramCompiler();
			return compiler.Run(args);
		}
	}
}
